/*
 *  Copyright 2024 Collate.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *  http://www.apache.org/licenses/LICENSE-2.0
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
import { APIRequestContext, Page } from '@playwright/test';
import { Operation } from 'fast-json-patch';
import { SERVICE_TYPE } from '../../constant/service';
import { ServiceTypes } from '../../constant/settings';
import { uuid } from '../../utils/common';
import { visitEntityPage } from '../../utils/entity';
import {
  EntityTypeEndpoint,
  ResponseDataType,
  ResponseDataWithServiceType,
} from './Entity.interface';
import { EntityClass } from './EntityClass';

export class StoredProcedureClass extends EntityClass {
  service: {
    name: string;
    serviceType: string;
    connection: {
      config: {
        type: string;
        scheme: string;
        username: string;
        authType: {
          password: string;
        };
        hostPort: string;
        supportsMetadataExtraction: boolean;
        supportsDBTExtraction: boolean;
        supportsProfiler: boolean;
        supportsQueryComment: boolean;
      };
    };
  };
  database: {
    name: string;
    service: string;
  };
  schema: {
    name: string;
    database: string;
  };
  entity: {
    name: string;
    databaseSchema: string;
    description: string;
    storedProcedureCode: {
      code: string;
    };
  };

  serviceResponseData: ResponseDataType = {} as ResponseDataType;
  databaseResponseData: ResponseDataWithServiceType =
    {} as ResponseDataWithServiceType;
  schemaResponseData: ResponseDataWithServiceType =
    {} as ResponseDataWithServiceType;
  entityResponseData: ResponseDataWithServiceType =
    {} as ResponseDataWithServiceType;

  constructor(name?: string) {
    super(EntityTypeEndpoint.StoreProcedure);

    this.service = {
      name: name ?? `pw-database-service-${uuid()}`,
      serviceType: 'Mysql',
      connection: {
        config: {
          type: 'Mysql',
          scheme: 'mysql+pymysql',
          username: 'username',
          authType: {
            password: 'password',
          },
          hostPort: 'mysql:3306',
          supportsMetadataExtraction: true,
          supportsDBTExtraction: true,
          supportsProfiler: true,
          supportsQueryComment: true,
        },
      },
    };

    this.database = {
      name: `pw-database-${uuid()}`,
      service: this.service.name,
    };

    this.schema = {
      name: `pw-database-schema-${uuid()}`,
      database: `${this.service.name}.${this.database.name}`,
    };

    this.entity = {
      name: `pw-stored-procedure-${uuid()}`,
      description: `Description for pw-stored-procedure-${uuid()}`,
      databaseSchema: `${this.service.name}.${this.database.name}.${this.schema.name}`,
      storedProcedureCode: {
        code: 'CREATE OR REPLACE PROCEDURE output_message(message VARCHAR)\nRETURNS VARCHAR NOT NULL\nLANGUAGE SQL\nAS\n$$\nBEGIN\n  RETURN message;\nEND;\n$$\n;',
      },
    };

    this.serviceCategory = SERVICE_TYPE.Database;
    this.type = 'Store Procedure';
    this.serviceType = ServiceTypes.DATABASE_SERVICES;
  }

  async create(apiContext: APIRequestContext) {
    const serviceResponse = await apiContext.post(
      '/api/v1/services/databaseServices',
      {
        data: this.service,
      }
    );
    const databaseResponse = await apiContext.post('/api/v1/databases', {
      data: this.database,
    });
    const schemaResponse = await apiContext.post('/api/v1/databaseSchemas', {
      data: this.schema,
    });
    const entityResponse = await apiContext.post('/api/v1/storedProcedures', {
      data: this.entity,
    });

    const service = await serviceResponse.json();
    const database = await databaseResponse.json();
    const schema = await schemaResponse.json();
    const entity = await entityResponse.json();

    this.serviceResponseData = service;
    this.databaseResponseData = database;
    this.schemaResponseData = schema;
    this.entityResponseData = entity;

    return {
      service,
      database,
      schema,
      entity,
    };
  }

  async patch({
    apiContext,
    patchData,
  }: {
    apiContext: APIRequestContext;
    patchData: Operation[];
  }) {
    const response = await apiContext.patch(
      `/api/v1/storedProcedures/name/${this.entityResponseData?.['fullyQualifiedName']}`,
      {
        data: patchData,
        headers: {
          'Content-Type': 'application/json-patch+json',
        },
      }
    );

    this.entityResponseData = await response.json();

    return {
      entity: this.entityResponseData,
    };
  }

  get() {
    return {
      service: this.serviceResponseData,
      database: this.databaseResponseData,
      schema: this.schemaResponseData,
      entity: this.entityResponseData,
    };
  }

  public set(data: {
    entity: ResponseDataWithServiceType;
    service: ResponseDataType;
    database: ResponseDataWithServiceType;
    schema: ResponseDataWithServiceType;
  }): void {
    this.entityResponseData = data.entity;
    this.serviceResponseData = data.service;
    this.databaseResponseData = data.database;
    this.schemaResponseData = data.schema;
  }

  async visitEntityPage(page: Page) {
    await visitEntityPage({
      page,
      searchTerm: this.entityResponseData?.['fullyQualifiedName'],
      dataTestId: `${
        this.entityResponseData.service.name ?? this.service.name
      }-${this.entityResponseData.name ?? this.entity.name}`,
    });
  }

  async delete(apiContext: APIRequestContext) {
    const serviceResponse = await apiContext.delete(
      `/api/v1/services/databaseServices/name/${encodeURIComponent(
        this.serviceResponseData?.['fullyQualifiedName']
      )}?recursive=true&hardDelete=true`
    );

    return {
      service: serviceResponse.body,
      entity: this.entityResponseData,
    };
  }
}
